from __future__ import annotations
from typing import Dict, List, Optional
from uuid import uuid4
from datetime import datetime

from models.assignment import Assignable, Assignment, Quiz, Project
from models.submission import Submission
from models.grade import Grade


class AssignmentManager:
    def __init__(self):
        self.assignments: Dict[str, Assignable] = {}
        self.submissions: Dict[str, Submission] = {}

    def create_assignment(self, assignment: Assignable = None, *args, **kwargs):
        """Create an assignment.

        支持两种用法：
        - 传入 Assignable 子类实例： create_assignment(assignment)
        - 传入参数： create_assignment(course_id, title, description, due_date, max_score, created_by=None)

        返回新创建的 Assignable 对象（成功）或 False/None（失败）。
        """
        # 如果传入的是 Assignable 实例，保持原行为
        if assignment is not None and isinstance(assignment, Assignable):
            if assignment.assignment_id in self.assignments:
                return False
            self.assignments[assignment.assignment_id] = assignment
            return assignment

        # 如果第一个位置参数不是 Assignable，但传入了一个值（例如 CLI 直接传 course_id 为第一个位置参数），
        # 则把它当作 course_id 使参数对齐。
        if assignment is not None and not isinstance(assignment, Assignable):
            # 把 assignment（实际上是 course_id 字符串）放到 args 前面以便后续处理
            args = (assignment,) + args

        # 否则按位置或关键字参数创建 Assignment
        # 期望参数顺序: course_id, title, description, due_date, max_score, created_by(optional)
        try:
            if args and len(args) >= 5:
                course_id, title, description, due_date_raw, max_score = args[:5]
                created_by = args[5] if len(args) > 5 else kwargs.get('created_by')
            else:
                course_id = kwargs.get('course_id') or (args[0] if len(args) >= 1 else None)
                title = kwargs.get('title') or (args[1] if len(args) >= 2 else None)
                description = kwargs.get('description') or (args[2] if len(args) >= 3 else None)
                due_date_raw = kwargs.get('due_date') or (args[3] if len(args) >= 4 else None)
                max_score = kwargs.get('max_score') or (args[4] if len(args) >= 5 else None)
                created_by = kwargs.get('created_by')

            if not all([course_id, title, description, due_date_raw, max_score is not None]):
                raise ValueError("缺少创建作业的必要参数")

            # 解析截止日期（如果是字符串）——更宽容地尝试多种格式
            due_date = None
            if isinstance(due_date_raw, datetime):
                due_date = due_date_raw
            elif isinstance(due_date_raw, str):
                parsers = [
                    lambda s: datetime.fromisoformat(s),
                    lambda s: datetime.strptime(s, "%Y-%m-%d"),
                    lambda s: datetime.strptime(s, "%Y/%m/%d"),
                    lambda s: datetime.strptime(s, "%Y.%m.%d"),
                ]
                for p in parsers:
                    try:
                        due_date = p(due_date_raw)
                        break
                    except Exception:
                        continue
            if due_date is None:
                raise ValueError(f"无法解析的截止日期格式: {due_date_raw}")

            # 生成 assignment_id
            aid = str(uuid4())
            # 创建 Assignment 对象
            assign = Assignment(
                assignment_id=aid,
                title=title,
                description=description,
                course_id=course_id,
                max_score=float(max_score),
                due_date=due_date,
                created_by=created_by
            )

            self.assignments[assign.assignment_id] = assign
            return assign

        except Exception as e:
            # 不要让异常破坏 CLI 交互，返回 None 并打印日志
            print(f"创建作业时出错: {e}")
            return None

    def get_assignment(self, assignment_id: str) -> Optional[Assignable]:
        return self.assignments.get(assignment_id)

    def delete_assignment(self, assignment_id: str) -> bool:
        if assignment_id in self.assignments:
            del self.assignments[assignment_id]
            # remove related submissions
            self.submissions = {k: v for k, v in self.submissions.items() if v.assignment_id != assignment_id}
            return True
        return False

    def submit_assignment(self, student_id: str, assignment_id: str, content: dict) -> Optional[Submission]:
        assignment = self.get_assignment(assignment_id)
        if not assignment:
            return None
        if not assignment.validate_submission(content):
            return None
        submission_id = str(uuid4())
        sub = Submission(submission_id=submission_id, student_id=student_id, assignment_id=assignment_id, content=content)
        sub.submit()
        # check late
        sub.check_if_late(assignment.due_date)
        self.submissions[submission_id] = sub
        return sub

    def get_submissions(self, assignment_id: str) -> List[Submission]:
        return [s for s in self.submissions.values() if s.assignment_id == assignment_id]

    def get_student_submissions(self, student_id: str) -> List[Submission]:
        return [s for s in self.submissions.values() if s.student_id == student_id]

    def grade_submission(self, submission_id: str, grader_id: Optional[str], score: float, feedback: Optional[str] = None) -> Optional[Grade]:
        sub = self.submissions.get(submission_id)
        if not sub:
            return None
        assignment = self.get_assignment(sub.assignment_id)
        if not assignment:
            return None
        # clamp score
        score = max(0.0, min(float(score), float(assignment.max_score)))
        sub.add_grade(score, feedback)
        grade = Grade(
            grade_id=str(uuid4()),
            student_id=sub.student_id,
            assignment_id=sub.assignment_id,
            grader_id=grader_id,
            course_id=assignment.course_id,
            max_points=assignment.max_score,
            numeric_grade=score,
            feedback=feedback or ""
        )
        return grade

    def get_assignments_by_course(self, course_id: str) -> List[Assignable]:
        """Get all assignments for a specific course."""
        return [assignment for assignment in self.assignments.values() if assignment.course_id == course_id]
# AssignmentManager implementation